package main

import (
	"fmt"
	"os"
)

// IBDFileInfo ibd文件的信息
type IBDFileInfo struct {
	PageInfos    []*PageInfo
	Size         int64
	TotalPageNum int
}

// PageInfo 每个页面的信息
type PageInfo struct {
	PageOffset int
	PageType   string
	PagePrev   int
	PageNext   int
	PageLSN    int64

	PageHeapTop  int
	PageNHeap    int
	PageFree     int
	PageGarbage  int
	PageRowNum   int
	PageMaxTrxId int64
	PageIndexId  int64
	PageLevel    int
}

func AnalyseIBDFile(path string) *IBDFileInfo {
	file, err := os.Open(path)
	if err != nil {
		fmt.Printf("Can't open file %s err: %v\n",
			path, err)
		return nil
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		fmt.Printf("Can't get file %s stat; err: %v\n",
			path, err)
		return nil
	}

	res := new(IBDFileInfo)
	res.Size = stat.Size()
	res.TotalPageNum = int(res.Size / PAGE_SIZE)
	res.PageInfos = make([]*PageInfo, res.TotalPageNum)

	// 每次读一个页 (16KB)
	buf := make([]byte, PAGE_SIZE)
	for i := 0; i < res.TotalPageNum; i++ {
		_, err := file.ReadAt(buf, int64(i)*PAGE_SIZE)
		if err != nil {
			fmt.Printf("Can't read file %s data; err: %v\n",
				path, err)
			return nil
		}
		res.PageInfos[i] = AnalysePage(&buf)
	}

	return res
}

func AnalysePage(page *[]byte) *PageInfo {
	res := new(PageInfo)
	fileHeader := (*page)[0:38]
	pageHeader := (*page)[38 : 38+56]

	res.PageOffset = HexToInt(fileHeader[4:8])
	res.PagePrev = HexToInt(fileHeader[8:12])
	res.PagePrev = HexToInt(fileHeader[12:16])
	res.PageLSN = HexToInt64(fileHeader[16:24])
	res.PageType = PageTypeMap[HexToInt(fileHeader[24:26])]

	res.PageHeapTop = HexToInt(pageHeader[2:4])
	res.PageNHeap = HexToInt(pageHeader[4:6])
	res.PageFree = HexToInt(pageHeader[6:8])
	res.PageGarbage = HexToInt(pageHeader[8:10])
	res.PageRowNum = HexToInt(pageHeader[16:18])
	res.PageMaxTrxId = HexToInt64(pageHeader[18:26])
	res.PageLevel = HexToInt(pageHeader[26:28])
	res.PageIndexId = HexToInt64(pageHeader[28:36])

	return res
}

func HexToInt(data []byte) int {
	return int(HexToInt64(data))
}

func HexToInt64(data []byte) int64 {
	n := len(data)
	var res int64 = 0
	for i, b := range data {
		res |= int64(b) << (n - i - 1)
	}
	return res
}
